package syM

import (
	"fmt"
	"db2s/Fp"
	"db2s/arg"
	"db2s/global"
	"db2s/go-log/log"
	"db2s/outPut"
	"db2s/parDef"
	"db2s/ref"
	mq "db2s/topic-mq"
	"reflect"
	"strings"
	"sync"
)

func podStructInit(v arg.InputTableAttributes) outPut.Struct {
	return outPut.Struct{
		Role:        v.TableAttributes.Role,
		IssueType:   "no",
		DataFixType: GlobalPConfigs.repair.DataFix,
		ExecResult:  "ok",
	}
}
func podObjectInit(v arg.InputTableAttributes) outPut.Object {
	return outPut.Object{
		Role:        v.TableAttributes.Role,
		IssueType:   "no",
		DataFixType: GlobalPConfigs.repair.DataFix,
		ExecResult:  "ok",
	}
}

// table sum pod Init
func (t CheckSchemaMetaData) podInit(seq int, v arg.InputTableAttributes) (result *outPut.TPod, err error) {
	var status int64
	result = &outPut.TPod{Seq: seq, Schema: v.Schema, Table: v.Table, Options: v.TableAttributes.Role, CheckMode: GlobalPConfigs.rules.CheckMode, IndexName: "missIndex", IndexType: "none", IndexCol: "none", Status: &status, Result: "ok"}
	switch GlobalPConfigs.rules.CheckMode {
	case "struct":
		result.Struct = podStructInit(v)
	case "object":
		result.Object = podObjectInit(v)
	}
	return
}
func dataFixBasePath(plan *SchedulePlan2) (path string, err error) {
	for _, v := range []string{global.DataFix, plan.Object.Schema, plan.Object.Role} {
		if path, err = global.JoinPath(path, v); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("join path fail. path is %v error info is %v", path, err))
			return
		}
	}
	if _, err = global.EnsurePathExists(path); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("path %v mkdir fail. error info is %v", path, err))
		return
	}
	return
}
func dataFixFileMany(plan *SchedulePlan2, path string) (err error) {
	var fileName string
	if fileName, err = global.JoinPath(path, GlobalPConfigs.repair.FixFileName); err != nil {
		return
	}
	if strings.EqualFold(fileName, schemaDataFixFileName) { //同一个文件
		plan.fixFile.SchemaFileName, plan.fixFile.SchemaFilePoint = fileName, schemaDataFixPoint
	} else {
		schemaDataFixPoint = outPut.NewFileOperations(outPut.Tex{FileName: fileName, RSymbol: "\n"})
		if err = schemaDataFixPoint.Init(); err != nil {
			return
		}
		plan.fixFile.SchemaFileName, schemaDataFixFileName = fileName, fileName
		plan.fixFile.SchemaFilePoint = schemaDataFixPoint
	}
	return
}
func dataFixFileSplit(plan *SchedulePlan2, path string) (err error) {
	for _, kk := range []string{fmt.Sprintf("%v.Create", plan.Object.Table), fmt.Sprintf("%v.Alter", plan.Object.Table)} {
		var localPath string
		if localPath, err = global.JoinPath(path, kk); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("join file path fail. path is %v file is %v error info is %v", path, kk, err))
			return
		}
		switch {
		case strings.HasSuffix(kk, ".Create"):
			plan.fixFile.CreateFileName = localPath
			plan.fixFile.CreateFilePoint = outPut.NewFileOperations(outPut.Tex{FileName: localPath, RSymbol: "\n"})
			if err = plan.fixFile.CreateFilePoint.Init(); err != nil {
				return
			}
		case strings.HasSuffix(kk, ".Alter"):
			plan.fixFile.AlterFileName = localPath
			plan.fixFile.AlterFilePoint = outPut.NewFileOperations(outPut.Tex{FileName: localPath, RSymbol: "\n"})
			if err = plan.fixFile.AlterFilePoint.Init(); err != nil {
				return
			}
		}
	}
	return
}
func InitDataFixFile(plan *SchedulePlan2) (err error) {
	var path string
	if path, err = dataFixBasePath(plan); err != nil {
		return
	}
	if getDataFixFileSingleFay() {
		if err = dataFixFileMany(plan, path); err != nil {
			return
		}
		return
	}
	if err = dataFixFileSplit(plan, path); err != nil {
		return
	}
	return
}

func tableStatusInit(plan2 *SchedulePlan2) {
	var (
		errEnd int64 = 0
		logSeq int64
	)
	plan2.Status.LogSeq = logSeq
	plan2.Status.ErrEnd = &errEnd
}
func initXlsResult(s *SchemaInitContext) *outPut.WorkSheetResult {
	return &outPut.WorkSheetResult{
		Data: outPut.SheetResultData{
			Seq:     s.Seq,
			Schema:  s.Schema,
			Table:   s.Table,
			Role:    s.Role,
			Mode:    GlobalPConfigs.rules.CheckMode,
			Comment: "",
			Struct:  outPut.StructResultSheet{},
		},
	}
}
func initXlsMiss(s *SchemaInitContext) *outPut.WorkSheetMiss {
	return &outPut.WorkSheetMiss{
		Data: outPut.SheetMiss{
			Schema: s.Schema,
			Table:  s.Table,
			Role:   s.Role,
		},
	}
}
func initXlsSwitch(s *SchemaInitContext) *outPut.WorkSheetSwitch {
	return &outPut.WorkSheetSwitch{
		Data: outPut.SheetSwitchData{
			Schema: s.Schema,
			Table:  s.Table,
			Role:   s.Role,
			Mode:   GlobalPConfigs.rules.CheckMode,
		},
	}
}
func initSendMsg(plan *SchedulePlan2) {
	plan.MQ.SendMsg = mq.ChanSendMsg{
		Table:             plan.Object.Table,
		MsgData:           plan.MQ.SendMsg.BaseMsg,
		MsgSuccessfulFunc: SendMsgExecSumFunc([]any{plan.MQ.FirstMsg.ProductMsg, int64(1)}),
		MsgFinishFunc:     SendMsgExecSumFunc([]any{[]any{plan.MQ.FirstMsg.CustomerObject}, int64(1)}),
		MsgProducer:       producer,
	}
}

func initXlsTerminal(_ *SchemaInitContext) *outPut.WorkSheetTerminal {
	return &outPut.WorkSheetTerminal{
		Data: outPut.TPod{},
	}
}
func initBaseSchedulePlanObject(s *SchemaInitContext) global.Object {
	return global.Object{TaskSeq: s.Seq, ObjectLogSeq: s.ObjectLogSeq, Schema: s.Schema, Table: s.Table, MSchema: s.MSchema, MTable: s.MTable,
		Role:  s.Role,
		Input: global.StartPartConfigInputP{SqlMode: GlobalPConfigs.rules.Struct.SqlMode},
	}
}
func optionsInit(s *SchemaInitContext) parDef.Options {
	return parDef.Options{
		Scn: GlobalPConfigs.rules.Struct.Scn,
		//StopTime: time.Duration(GlobalPConfigs.rules.Sync.),
		WhereSql:     s.TableAttributes.WhereSql,
		WhereAdd:     Fp.ParseWhereClause(s.TableAttributes.WhereAdd),
		SqlMode:      GlobalPConfigs.rules.Struct.SqlMode,
		SqlLogBin:    GlobalPConfigs.rules.Struct.SqlLogBin,
		RepairMethod: GlobalPConfigs.repair.DataFix,
	}
}
func srcParameterInit(s *SchemaInitContext) parDef.Parameter {
	return parDef.Parameter{
		Con:     GlobalPConfigs.db.Source.GetDB("single"),
		Object:  parDef.Object{Schema: s.Schema, Table: s.Table},
		Options: optionsInit(s),
	}
}
func dstParameterInit(s *SchemaInitContext) parDef.Parameter {
	return parDef.Parameter{
		Con:     GlobalPConfigs.db.Target.GetDB("single"),
		Object:  parDef.Object{Schema: s.MSchema, Table: s.MTable},
		Options: optionsInit(s),
	}
}
func newSyncSchedulePlan(s *SchemaInitContext) *SchedulePlan2 {
	plan2 := &SchedulePlan2{
		Object:          initBaseSchedulePlanObject(s),
		SrcParameter:    srcParameterInit(s),
		DstParameter:    dstParameterInit(s),
		XlsResult:       initXlsResult(s),
		XlsSwitch:       initXlsSwitch(s),
		XlsMiss:         initXlsMiss(s),
		XlsTerminal:     initXlsTerminal(s),
		TPod:            s.TPod,
		TableAttributes: s.TableAttributes,
	}
	tableStatusInit(plan2)
	return plan2
}
func speedInit(plan *SchedulePlan2) mq.SpeedLimit {
	q := mq.NewSpeedMonitor(plan.Object.Schema, plan.Object.Table, 10)
	return q
}
func initStructSchedulePlan(s *SchemaInitContext) (plan2 *SchedulePlan2) {
	plan2 = newSyncSchedulePlan(s)
	plan2.MQ = mq.NewMonitorMsgMq()
	plan2.MQ.Speed = speedInit(plan2)
	plan2.MQ.SpeedLimitSwitch = false
	initSendMsg(plan2)
	subTaskInit(plan2)
	if strings.EqualFold(GlobalPConfigs.repair.DataFix, "file") {
		InitDataFixFile(plan2)
	}
	if s.TPod.TableMiss {
		plan2.Object.TableMiss = true
	}
	return
}
func initObjectSchedulePlan(s *SchemaInitContext) (plan2 *SchedulePlan2) {
	plan2 = newSyncSchedulePlan(s)
	plan2.MQ = mq.NewMonitorMsgMq()
	plan2.MQ.Speed = speedInit(plan2)
	plan2.MQ.SpeedLimitSwitch = false
	initSendMsg(plan2)
	subTaskInit(plan2)
	if strings.EqualFold(GlobalPConfigs.repair.DataFix, "file") {
		InitDataFixFile(plan2)
	}
	if s.TPod.TableMiss {
		plan2.Object.TableMiss = true
	}
	return plan2
}

func InitToSchedulePlan2TableExecFunc(event string, f any) (r any) {
	_, chanParameter, _ := schemaMetaExecFuncParameterSplit(f.([]any))
	if funcName, ok := ScheduleTaskInitFuncRef[GlobalPConfigs.rules.CheckMode]; ok {
		if funcName.Kind() == reflect.Func {
			arguments := []reflect.Value{
				reflect.ValueOf(chanParameter)}
			m1 := funcName.Call(arguments)
			var m2 *SchedulePlan2
			for i := 0; i < len(m1); i++ {
				m2 = m1[i].Interface().(*SchedulePlan2)
			}
			ChanSchedulePlanTable <- m2
		}
	}
	return
}
func InitToSchedulePlan2TableFinishFunc(event string, _ any) (r any) {
	ref.MyWaitDel("scheduleObject")
	close(ChanSchedulePlanTable)
	return
}
func plan2TableScheduleObjectInit(f2 *SchemaInitContext, ChanSchedulePlanTable any) (send mq.MonitorMsgMq) {
	ref.MyWaitAdd("scheduleObject")
	var wg sync.WaitGroup
	return mq.NewMonitorMsgMq1().SetSendMsg(f2.ChanSchemaContext).SetCurryLimit(mq.SendCurryLimit{
		Wg:       &wg,
		Switch:   true,
		CurrySum: make(chan struct{}, GlobalPConfigs.rules.Mtc),
	}).SetNormalQuitFunc(global.EFunc{
		FuncName: InitToSchedulePlan2TableFinishFunc}).SetExecFunc([]global.EFunc{
		{FuncName: InitToSchedulePlan2TableExecFunc, Params: []any{ChanSchedulePlanTable}}})
}

func InitToSchedulePlan2Table() chan any {
	var f2 = global.GetIoc().GetBean("schemaInitContext").(*SchemaInitContext)
	ChanSchedulePlanTable = make(chan any, f2.TableSum)
	go plan2TableScheduleObjectInit(f2, ChanSchedulePlanTable).NormalChanSendMsg("syMInitToSchedulePlan2Table")
	return ChanSchedulePlanTable
}
